package com.ykh.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.xml.transform.Result;
import java.lang.reflect.Array;
import java.util.Arrays;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: YKH
 * Date: 2022-08-24
 * Time: 18:09
 */

/**
 * 创建一个切面类的步骤 :
 * 1. 创建一个类, 用@Aspect 将其标识为切面类
 * 2. 在其中写 增强方法, 用@Beafore ,@After等进行注解
 * 3. 注解时 设置切入点表达式 ( 可先先使用 @Pointcut 进行重用)
 * 4. 每个参数中通过 JoinPoint对象获取连接点的相关信息
 */
@Component
@Aspect //将这个类标识为切面类
public class LoggerAop {
    //声明: 切入点的重用
    @Pointcut("execution(* com.ykh.spring.aop.*.*(..))")
    public void pointCut() {
    }

    //前置通知
    @Before("pointCut()")
    public void beforeNotice(JoinPoint joinPoint) {
        //获取连接点对应方法的签名
        Signature signature = joinPoint.getSignature();
        //获取连接点对应方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAop 前置通知" + signature + "参数" + Arrays.toString(args));
    }

    //后置通知
    @After("pointCut()")
    public void afterNotice(JoinPoint joinPoint) {
        //获取连接点对应方法的签名
        Signature signature = joinPoint.getSignature();
        //获取连接点对应方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAop 后置通知" + signature + "参数" + Arrays.toString(args));
    }

    //返回通知
    @AfterReturning(value = "pointCut()", returning = "result")
    public void returnNotice(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->返回通知，方法名：" + methodName + "，结果：" + result);
    }

    //抛出异常通知
    @AfterThrowing(value = "pointCut()", throwing = "ex")
    public void throwingExceptionNotice(JoinPoint joinPoint, Throwable ex) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->异常通知，方法名：" + methodName + "，异常：" + ex);
    }

    //环绕通知 , 返回值类型要和目标方法一致
    @Around(value = "pointCut()")
    public Object aroundNotice(ProceedingJoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        Object result = null;
        try {
            System.out.println("环绕通知-->目标对象方法执行之前");
            //目标方法的执行，目标方法的返回值一定要返回给外界调用者
            result = joinPoint.proceed();
            System.out.println("环绕通知-->目标对象方法返回值之后");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知-->目标对象方法出现异常时");
        } finally {
            System.out.println("环绕通知-->目标对象方法执行完毕");
        }
        return result;
    }
}
